package com.bifrost.service.impl;

import com.bifrost.constant.*;
import com.bifrost.constant.OrderType;
import com.bifrost.entity.*;
import com.bifrost.exception.BonusPointsNotEnoughException;
import com.bifrost.exception.InventoryUnavailableException;
import com.bifrost.model.promotion.PromotionModel;
import com.bifrost.model.promotion.SeckillModel;
import com.bifrost.model.promotion.SeckillTimeModel;
import com.bifrost.model.ro.OrderPointRO;
import com.bifrost.repository.PromotionConstraintRepository;
import com.bifrost.repository.PromotionRepository;
import com.bifrost.repository.PromotionSkuRepository;
import com.bifrost.repository.SkuOptionValueRepository;
import com.bifrost.service.*;
import com.bifrost.service.ProductService;
import com.bifrost.util.DateUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author rangf
 * @date 2018/3/5
 */
@Service
public class PromotionServiceImpl implements PromotionService {

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyMMddHH");
    private Logger logger = LoggerFactory.getLogger(PromotionServiceImpl.class);

    @Autowired
    PromotionRepository promotionRepository;
    @Autowired
    PromotionSkuRepository promotionSkuRepository;
    @Autowired
    ProductSkuService productSkuService;
    @Autowired
    ProductService productService;
    @Autowired
    SkuOptionValueRepository skuOptionValueRepository;
    @Autowired
    CountryAreaService countryAreaService;
    @Autowired
    OrderService orderService;
    //@Autowired
    //CheckoutService checkoutService;
    @Autowired
    OrderStatusHistoryService statusHistoryService;
    @Autowired
    BonusPointsService bonusPointsService;
    @Autowired
    ShippingService shippingService;
    @Autowired
    PromotionConstraintRepository constraintRepository;
    @Autowired
    RedisTemplate<String, String> redis;

    /**
     * 获取促销列表
     *
     * @param type  促销类型
     * @param store store
     * @return
     */
    @Override
    public List<PromotionModel> list(MerchantStore store, String type) {
        List<Promotion> promotionList = promotionRepository.getPromotionsList(store.getMerchantId(), type);
        Date now = new Date();
        promotionList = promotionList.stream()
                .filter(p -> now.before(p.getEndTime()))
                .filter(p -> now.after(p.getStartTime()))
                .sorted(Comparator.comparing(Promotion::getStartTime))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(promotionList)) {
            List<PromotionModel> itemModels = new ArrayList<>();
            for (Promotion promotion : promotionList) {
                List<PromotionSku> promotionSkuList = promotionSkuRepository.findAllByPromotion(promotion);
                if (CollectionUtils.isNotEmpty(promotionSkuList)) {
                    promotionSkuList = promotionSkuList.stream().
                            filter(promotionSku -> Boolean.FALSE.equals(promotionSku.getArchived())).collect(Collectors.toList());
                }
                if (CollectionUtils.isNotEmpty(promotionSkuList)) {
                    Map<Product, List<PromotionSku>> map = promotionSkuList.stream().collect(Collectors.groupingBy(PromotionSku::getProduct));
                    for (Product product : map.keySet()) {
                        List<PromotionSku> promotionSkus = map.get(product);
                        if (CollectionUtils.isNotEmpty(promotionSkus)) {
                            PromotionSku promotionSku = promotionSkus.get(0);
                            itemModels.add(convertToPromotionModel(promotion, promotionSku, true));
                        }
                    }
                }
            }
            return itemModels;
        }
        return null;
    }

    /**
     * 获取促销明细
     *
     * @param promotionSkuId promotionSkuId
     * @return
     */
    @Override
    public PromotionModel promotion(Long promotionSkuId) {
        PromotionSku first = promotionSkuRepository.findOne(promotionSkuId);
        if (null != first) {
            Promotion promotion = first.getPromotion();
            if (null != promotion) {
                PromotionModel model = convertToPromotionModel(promotion, first, false);
                List<PromotionSku> promotionSkuList = promotionSkuRepository.findAllByPromotionAndProduct(promotion, first.getProduct());
                if (CollectionUtils.isNotEmpty(promotionSkuList)) {
                    PromotionSku ps = promotionSkuList.get(0);
                    List<Map> skuList = new ArrayList<>();
                    List<Map> optionList = new ArrayList<>();
                    List<String> optionIndexList = new ArrayList<>();
                    for (PromotionSku promotionSku : promotionSkuList) {
                        ProductSku productSku = promotionSku.getProductSku();
                        Map sku = new HashMap(8);
                        sku.put("id", productSku.getSkuId());
                        if (PromotionType.EXCHANGE.equalsIgnoreCase(promotion.getType())) {
                            sku.put("price", promotionSku.getPromotionPoint());
                        } else {
                            sku.put("price", promotionSku.getPromotionPrice().multiply(BigDecimal.valueOf(100)));
                        }
                        sku.put("stock_num", promotionSku.getInventoryAvailable());
                        List<SkuOptionValue> skuOptionValueList = skuOptionValueRepository.findAllByProductSku(productSku);
                        if (CollectionUtils.isNotEmpty(skuOptionValueList)) {
                            List<ProductOptionValue> productOptionList = skuOptionValueList.stream().map(SkuOptionValue::getProductOptionValue).collect(Collectors.toList());
                            for (int i = 1; i <= productOptionList.size(); i++) {
                                ProductOptionValue productOptionValue = productOptionList.get(i - 1);
                                ProductOption productOption = productOptionValue.getProductOption();
                                Long id = productOptionValue.getProductOptionValueId();
                                sku.put("s" + i, id);
                                String name = productOption.getAttributeName();
                                if (!optionIndexList.contains(name)) {
                                    Map opt = new HashMap();
                                    opt.put("k", name);
                                    opt.put("v", new ArrayList<>());
                                    opt.put("k_s", "s" + i);
                                    optionList.add(opt);
                                    optionIndexList.add(name);
                                }
                                int index = optionIndexList.indexOf(name);
                                Map option = optionList.get(index);
                                Map optValue = new HashMap(4);
                                optValue.put("id", productOptionValue.getProductOptionValueId());
                                optValue.put("name", productOptionValue.getAttributeValue());
                                List optList = ((List) option.get("v"));
                                if (!optList.contains(optValue)) {
                                    optList.add(optValue);
                                }
                            }
                        }
                        skuList.add(sku);
                    }
                    model.setSkus(skuList);
                    model.setProductOptions(optionList);
                    return model;
                }
            }
        }
        return null;
    }

    private PromotionModel convertToPromotionModel(Promotion promotion, PromotionSku promotionSku, boolean isUsedToList) {
        ProductSku sku = promotionSku.getProductSku();
        Product product = sku.getProduct();
        Date startTime = promotion.getStartTime();
        Date endTime = promotion.getEndTime();
        PromotionModel model = new PromotionModel();
        model.setProductSkuId(sku.getSkuId());
        model.setPromotionId(promotion.getId());
        model.setPromotionSkuId(promotionSku.getId());
        model.setPromotionType(promotion.getType());
        model.setSkuName(product.getProductName());
        model.setLongDescription(sku.getLongDescription());
        if (!isUsedToList) {
            model.setImages(productSkuService.getDefaultSkuPrimaryMedias(sku));
        }else {
            model.setImage(productSkuService.getDefaultSkuMedia(sku));
        }
        model.setShippingType(sku.getFulfillmentType());
        model.setPrice(sku.getRetailPrice());
        if (PromotionType.EXCHANGE.equalsIgnoreCase(promotion.getType())) {
            model.setBonusPoints(promotionSku.getPromotionPoint());
        } else {
            model.setPromotionPrice(promotionSku.getPromotionPrice());
            if (Boolean.TRUE.equals(promotionSku.getHasLimit())) {
                model.setLimit(true);
                model.setLimitQuantity(promotionSku.getLimitQuantity());
            }
            if (PromotionType.GROUPON.equalsIgnoreCase(promotion.getType())) {
                model.setGrouponQuantity(promotion.getGrouponQuantity());
            }
        }
        model.setAvaliable(isActive(promotion));
        model.setStartTime(startTime);
        model.setEndTime(endTime);
        Long productId = promotionSku.getProduct().getProductId();
        model.setStock(promotionSkuRepository.sumStock(promotion.getId(), productId));
        Integer stock = promotionSkuRepository.sumStockAviliable(promotion.getId(), productId);
        stock=stock==null?0:stock;
        model.setStockAvalible(stock);
        Date now = new Date();
        if (now.before(startTime)) {
            model.setStatus(PromotionStatus.NOTSTART);
        } else if (now.after(endTime)) {
            model.setStatus(PromotionStatus.END);
        } else {
            if (stock <= 0) {
                model.setStatus(PromotionStatus.EMPTY);
            } else {
                model.setAvaliable(true);
                model.setStatus(PromotionStatus.START);
            }
        }
        return model;
    }

    private boolean checkStock(PromotionSku promotionSku) {
        Integer stock = promotionSku.getInventoryAvailable();
        if (stock == null || stock <= 0) {
            return false;
        }
        return true;
    }

    /**
     * 获取当天秒杀List
     *
     * @param storeId
     * @return
     */
    @Override
    public List<SeckillTimeModel> seckillTimes(Long storeId) {
        if (storeId != null) {
            List<Promotion> promotionList = promotionRepository.getSeckillListOfMerchantStore(storeId);
            if (CollectionUtils.isNotEmpty(promotionList)) {
                List<Promotion> promotions = new ArrayList<>();
                Date now = new Date();
                for (Promotion p : promotionList) {
                    //已经开始的活动
                    if(DateUtils.isActive(p.getStartTime(),p.getEndTime())){
                        promotions.add(p);
                    }//当天即将开始的活动
                    else if(DateUtils.formatDate(now,DateUtils.DATE_PATTERN).equals(DateUtils.formatDate(p.getStartTime(),DateUtils.DATE_PATTERN))){
                        promotions.add(p);
                    }

                   /* Date end = p.getEndTime();
                    //所有未结束（已开始、未开始）
                    if(now.before(end))
                            promotions.add(p);
                    now.setHours(0);
                    now.setMinutes(0);
                    now.setSeconds(0);
                    //当天已结束 或 当天将要结束
                    if(end.after(now))
                        if(!promotions.contains(p))
                            promotions.add(p);*/
                }
                List<SeckillTimeModel> models = new ArrayList<>();
                promotions = promotions.stream().sorted(Comparator.comparing(Promotion::getStartTime)).collect(Collectors.toList());
                for (Promotion promotion : promotions) {
                    SeckillTimeModel model = new SeckillTimeModel();
                    model.setStartTime(promotion.getStartTime());
                    model.setEndTime(promotion.getEndTime());
                    models.add(model);
                }
                return models;
            }
        }
        return null;
    }

    /**
     * 根据秒杀开始秒杀时间获取秒杀model
     *
     * @param store
     * @param startTime
     * @return
     */
    @Override
    public SeckillModel seckillList(MerchantStore store, Date startTime) {
        Promotion promotion = promotionRepository.getSeckillListByStoreAndStartTime(store, startTime);
        if (null != promotion) {
            SeckillModel model = new SeckillModel();
            model.setStartTime(promotion.getStartTime());
            model.setEndTime(promotion.getEndTime());
            model.setPromotions(new ArrayList<>());
            List<PromotionSku> promotionSkuList = promotion.getPromotionSkus();
            if (CollectionUtils.isEmpty(promotionSkuList)) {
                return null;
            }
            Map<Product, List<PromotionSku>> map = promotionSkuList.stream().
                    filter(promotionSku -> Boolean.FALSE.equals(promotionSku.getArchived())).collect(Collectors.groupingBy(PromotionSku::getProduct));
            if(!map.isEmpty()){
                for (Product product : map.keySet()) {
                    List<PromotionSku> promotionSkus = map.get(product);
                    if (CollectionUtils.isNotEmpty(promotionSkus)) {
                        PromotionSku first = promotionSkus.get(0);
                        model.getPromotions().add(convertToPromotionModel(promotion, first, true));
                    }
                }
            }
            return model;
        }
        return null;
    }

    /**
     * 查找指定的促销
     *
     * @param promotionId
     * @return
     */
    @Override
    public Promotion find(Long promotionId) {
        return promotionRepository.findOne(promotionId);
    }

    /**
     * 促销活动是否还有效
     *
     * @param promotion
     * @return
     */
    @Override
    public boolean isActive(Promotion promotion) {
        return promotion.isActive();
    }

    /**
     * 查询指定sku某促销的信息
     *
     * @param promotion
     * @param sku
     * @return
     */
    @Override
    public PromotionSku findByPromotionAndSku(Promotion promotion, ProductSku sku) {
        return promotionSkuRepository.findFirstByPromotionAndProductSku(promotion, sku);
    }

    @Override
    public boolean isAvailable(Promotion promotion, ProductSku sku) {
        Assert.notNull(promotion, "促销活动不能为空");
        Assert.notNull(sku, "商品sku不能为空");
        if (!promotion.isActive() || Boolean.TRUE.equals(promotion.isArchived())) {
            return false;
        }
        PromotionSku promotionSku = findByPromotionAndSku(promotion, sku);
        if (promotionSku != null) {
            Integer qty = promotionSku.getInventoryAvailable();
            return (qty != null && qty > 0);
        }
        return false;
    }

    @Override
    public boolean isAvailable(Long promotionId, Long skuId) {
        return isAvailable(find(promotionId), productSkuService.getSkuById(skuId));
    }

    @Override
    public PromotionSku findById(Long promotionSkuId) {
        if (promotionSkuId != null) {
            return promotionSkuRepository.findOne(promotionSkuId);
        }
        return null;
    }

    @Override
    public PromotionSku save(PromotionSku promotionSku) {
        return promotionSkuRepository.save(promotionSku);
    }

    /**
     * decrementInventory
     *
     * @param promotionSku
     * @param quantity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int decrementInventory(PromotionSku promotionSku, Integer quantity) {
        return promotionSkuRepository.decrementInventory(promotionSku.getId(), quantity);
    }

    /**
     * incrementInventory
     *
     * @param promotionSku
     * @param quantity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int incrementInventory(PromotionSku promotionSku, Integer quantity) {
        return promotionSkuRepository.incrementInventory(promotionSku.getId(), quantity);
    }


    /**
     * 参数校验:积分兑换
     *
     * @param ro ro
     * @return
     */
    @Override
    public String validateExchangeParam(OrderPointRO ro) {
        if (StringUtils.isEmpty(ro.getStreet()) || StringUtils.isEmpty(ro.getName()) || StringUtils.isEmpty(ro.getPhone())) {
            return "请完善收货信息";
        }
        if (StringUtils.isEmpty(ro.getShippingType())) {
            return "请选择配送方式";
        }
        if (ro.getPromotionId() == null || ro.getProductSkuId() == null) {
            return "兑换失败";
        }
        if (ro.getState() == null || ro.getCity() == null || ro.getDistrict() == null) {
            return "请填写收货地址";
        }
        return null;
    }

    /**
     * 积分兑换校验
     *
     * @param customer     customer
     * @param promotionSku promotionSku
     * @param ro           ro
     * @return
     */
    @Override
    public String validateExchange(Customer customer, PromotionSku promotionSku, OrderPointRO ro) {
        Promotion promotion = promotionSku.getPromotion();
        BigInteger bonusPoints = customer.getBonusPoints();
        Integer quantity = ro.getQuantity();
        Integer stock = promotionSku.getInventoryAvailable();
        if (null == bonusPoints || bonusPoints.compareTo(promotionSku.getPromotionPoint()) < 0) {
            return "积分不足";
        }
        if (!promotion.isActive()) {
            return "活动已失效";
        }
        if (Boolean.TRUE.equals(promotionSku.getHasLimit())) {
            if (promotionSku.getLimitQuantity().compareTo(quantity) < 0) {
                return String.format("每人限兑换%d件商品", promotionSku.getLimitQuantity());
            }
        }
        if (!checkStock(promotionSku) || stock.compareTo(quantity) < 0) {
            return "库存不足";
        }
        return null;
    }

    /**
     * 积分兑换
     *
     * @param customer customer
     * @param ro       ro
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Order exchange(Customer customer, PromotionSku promotionSku, OrderPointRO ro) throws InventoryUnavailableException {
            validateInventory(customer,ro,promotionSku);
        int result = decrementInventory(promotionSku, ro.getQuantity());
        if (result != 1) {
            throw new RuntimeException("decrement stock failed");
        }
        try {
            Order o = saveOrder(customer, promotionSku, ro);
            bonusPointsService
                    .decrementBonusPoints(customer, o.getOrderNumber(), null, promotionSku.getPromotionPoint(),
                            BonusPointsSource.MALL, BonusPointsUpdatedDetail.EXCHANGE_PRODUCT, "积分兑换商品");
            statusHistoryService.writeHistory(o, "积分兑换订单,系统自动确认");
            return o;
        } catch (BonusPointsNotEnoughException e) {
            throw new RuntimeException("积分不足：" + customer.getBonusPoints() + "所需积分：" + promotionSku.getPromotionPoint());
        } /*catch (PricingException e){
            throw new RuntimeException("计算运费失败");
        }*/
    }

    @Transactional(rollbackFor = Exception.class)
    public void validateInventory(Customer customer, OrderPointRO ro, PromotionSku promotionSku) throws InventoryUnavailableException {
        Product product = promotionSku.getProduct();
        Integer quantity = ro.getQuantity();
        Integer stock = promotionSku.getInventoryAvailable();
        if (!checkStock(promotionSku) || stock.compareTo(quantity) < 0) {
            throw new InventoryUnavailableException("库存不足");
        }
        if (Boolean.TRUE.equals(promotionSku.getHasLimit())) {
            Promotion promotion = promotionSku.getPromotion();
            PromotionConstraint constraint = constraintRepository.findByCustomerAndPromotionAndProductId(customer, promotionSku.getPromotion(), product.getProductId());
            if(constraint!=null){
                if(promotionSku.getLimitQuantity().compareTo(constraint.getQuantity()+quantity)<0){
                    throw new InventoryUnavailableException(String.format("每人限兑换%d件商品", promotionSku.getLimitQuantity()));
                }
                constraint.setQuantity(constraint.getQuantity()+quantity);
            }else {
                constraint = new PromotionConstraint();
                constraint.setQuantity(quantity);
                constraint.setCustomer(customer);
                constraint.setProductId(product.getProductId());
                constraint.setPromotion(promotionSku.getPromotion());
                try {
                    constraintRepository.save(constraint);
                } catch (Exception e) {
                    logger.error("数量超过购买限制"+ e.getMessage(),e);
                    throw new InventoryUnavailableException("数量超过购买限制");
                }
            }
        }
    }

    protected Order saveOrder(Customer customer, PromotionSku promotionSku, OrderPointRO ro)/* throws PricingException*/ {
        Order order = new Order();
        order.setIsBonusPointsUsed(Boolean.TRUE);
        order.setBonusPointsUsed(ro.getBounsPoints());
        Date date = new Date();
        order.setCreatedTimestamp(date);
        order.setDatePurchased(date);
        order.setCustomer(customer);
        order.setCustomerAgreed(Boolean.TRUE);
        order.setCustomerMessage(ro.getMessage());
        order.setDeliveryState(countryAreaService.getArea(ro.getState()));
        order.setDeliveryCity(countryAreaService.getArea(ro.getCity()));
        CountryArea deliveryDistrict = countryAreaService.getArea(ro.getDistrict());
        order.setDeliveryDistrict(deliveryDistrict);
        order.setDeliveryStreet(ro.getStreet());
        order.setDeliveryTelephonePrimary(ro.getPhone());
        order.setDeliveryLastName(ro.getName());
        order.setDeliveryPhoneSecondary(ro.getPhone());
        order.setGiftOrder(Boolean.FALSE);
        MerchantStore store = customer.getMerchantStore();
        order.setMerchantStore(store);
        order.setOrderItems(new ArrayList<>());
        order.setOrderType(OrderType.EXCHANGE);
        order.setShippingType(ro.getShippingType());
        order.setSubTotal(BigDecimal.ZERO);
        order.setOrderTotal(BigDecimal.ZERO);
        order.setTotalOffer(BigDecimal.ZERO);
        order.setTotalService(BigDecimal.ZERO);
        order.setOrderStatus(OrderStatus.CONFIRMED);
        order.setCommentStatus(CommentStatus.NOT_COMMENT);
        Promotion promotion = promotionSku.getPromotion();
        ProductSku sku = promotionSku.getProductSku();
        order.setDescription(sku.getSkuName());
        OrderItem oi = new OrderItem();
        oi.setOrder(order);
        oi.setSkuId(sku.getSkuId());
        oi.setSkuName(sku.getSkuName());
        oi.setProductId(sku.getProduct().getProductId());
        oi.setBonusPoints(promotionSku.getPromotionPoint().intValue());
        oi.setRetailPrice(sku.getRetailPrice());
        oi.setSkuPrice(sku.getSalePrice());
        if (Boolean.TRUE.equals(promotionSku.getHasLimit())) {
            oi.setSkuQuantity(promotionSku.getLimitQuantity());
        } else {
            oi.setSkuQuantity(ro.getQuantity());
        }
        oi.setIsPromoApplied(Boolean.TRUE);
        oi.setShipShare(BigDecimal.ZERO);
        oi.setSkuQuantity(ro.getQuantity());
        oi.setUpdatedTimestamp(date);
        oi.setAppliedPromoId(promotion.getId());
        oi.setOrderItemType(sku.getItemType());
        oi.setSkuCost(sku.getSkuCost());
        oi.setSkuUPC(sku.getSkuUpc());
        oi.setExternalId(sku.getExternalId());
        oi.setHasGift(Boolean.FALSE);
        oi.setCategoryId(sku.getCategory().getCategoryId());
        oi.setShipShare(BigDecimal.ZERO);
        oi.setShipShare(BigDecimal.ZERO);
        order.getOrderItems().add(oi);
        /*if (ShipmentMode.STORE.equalsIgnoreCase(store.getShipmentMode())) {
            order.setTotalShippingFee(shippingService.shippingPricingByStoreTemplate(store, order));
        }
        else if (ShipmentMode.SKU.equalsIgnoreCase(store.getShipmentMode())) {
            order.setTotalShippingFee(shippingService.shippingPricingBySkuTemplate(order, deliveryDistrict));
        } else if (ShipmentMode.MIXED.equalsIgnoreCase(store.getShipmentMode())) {
            order.setTotalShippingFee(shippingService.shippingPricingByMixedTemplate(order, deliveryDistrict));
        } else {
            order.setTotalShippingFee(BigDecimal.ZERO);
        }
        order.getOrderItems().get(0).setShipShare(order.getTotalShippingFee());*/
        order.setTotalShippingFee(BigDecimal.ZERO);
        Order dbOrder = orderService.save(order);
        dbOrder.setOrderNumber(String.format("%s%d%d", LocalDateTime.now().format(FORMATTER), dbOrder.getMerchantStore().getMerchantId(), dbOrder.getOrderId()));
        return orderService.save(dbOrder);
    }
}
